package xin.yangshuai.forest01.client;

import com.dtflys.forest.annotation.*;
import com.dtflys.forest.backend.httpclient.HttpClient;
import com.dtflys.forest.callback.OnError;
import com.dtflys.forest.callback.OnLoadCookie;
import com.dtflys.forest.callback.OnSaveCookie;
import com.dtflys.forest.callback.OnSuccess;
import com.dtflys.forest.http.ForestRequest;
import com.dtflys.forest.http.ForestResponse;
import com.dtflys.forest.http.Lazy;
import xin.yangshuai.common01.entity.BaseResult;
import xin.yangshuai.common01.entity.Dept;
import xin.yangshuai.common01.entity.User;
import xin.yangshuai.forest01.interceptor.SimpleInterceptor;

import java.util.List;
import java.util.Map;

// 整个接口下的所有方法请求都默认绑定该根地址
//@Address(host = "127.0.0.1", port = "8080")
public interface MyClient {

    @Get("http://localhost:8080/hello")
    String helloForest();

    @Request("http://localhost:28080/basic01/info")
    String simpleRequest();

    @Request(
            url = "http://localhost:8080/hello/user",
            headers = "Accept: text/plain"
    )
    String sendRequest(@Query("uname") String username);

    /**
     * 通过在 @Request 注解的 type 属性中定义字符串模板
     * 在字符串模板中引用方法的参数
     */
    @Request(
            url = "http://localhost:28080/basic01/info",
            type = "{type}"
    )
    String simpleRequest(@Var("type") String type);

    /**
     * 整个完整的URL都通过参数传入
     * {0}代表引用第一个参数
     */
    @Get("{0}")
    String send1(String myURL);

    /**
     * 整个完整的URL都通过 @Var 注解修饰的参数动态传入
     */
    @Get("{myURL}")
    String send2(@Var("myURL") String myURL);

    /**
     * 通过参数转入的值作为URL的一部分
     */
    @Get("http://{myURL}/abc")
    String send3(@Var("myURL") String myURL);

    /**
     * 参数转入的值可以作为URL的任意一部分
     */
    @Get("http://localhost:28080/basic01/{myURL}?a=1&b=2")
    String send4(@Var("myURL") String myURL);

    // 使用方法上的根地址
    // 最终URL: http://192.168.0.1:7000/data3
    @Post("/basic01/info")
    @Address(host = "localhost", port = "28080")
    String sendData3();

    // 也是通过 @Address 注解来绑定动态地址来源
    // 每次调用该方法，都可能是不同的根地址
    @Post("/basic01/info")
    @Address(source = MyAddressSource.class)
    String sendData();

    /**
     * 直接在url字符串的问号后面部分直接写上 参数名=参数值 的形式
     * 等号后面的参数值部分可以用 {变量名} 这种字符串模板的形式替代
     * 在发送请求时会动态拼接成一个完整的URL
     * 使用这种方式需要通过 @Var 注解或全局配置声明变量
     */
    @Get("http://localhost:28080/basic01/info?a={a}&b={b}&id=0")
    String send2(@Var("a") String a, @Var("b") String b);


    /**
     * 如果一个一个变量包含多个Query参数，比如: "a=1&b=2&c=3"
     * 为变量 parameters 的字符串值
     * 就用 ${变量名} 这种字符串模板格式
     * 使用这种方式需要通过 @Var 注解或全局配置声明变量
     */
    @Get("http://localhost:28080/basic01/info?${parameters}")
    String send5(@Var("parameters") String parameters);


    /**
     * 使用 @Query 注解，可以直接将该注解修饰的参数动态绑定到请求url中
     * 注解的 value 值即代表它在url的Query部分的参数名
     */
    @Get("http://localhost:28080/basic01/info?id=0")
    String send(@Query("a") String a, @Query("b") String b);

    /**
     * 使用 @Query 注解，可以修饰 Map 类型的参数
     * 很自然的，Map 的 Key 将作为 URL 的参数名， Value 将作为 URL 的参数值
     * 这时候 @Query 注解不定义名称
     */
    @Get("http://localhost:28080/basic01/info?id=0")
    String send1(@Query Map<String, Object> map);

    /*
     * 接受列表参数为URL查询参数
     */
    @Get("http://localhost:28080/basic01/info")
    String send1(@Query("id") List idList);

    @Get("http://localhost:28080/basic01/info")
    String send(@JSONQuery("id") List idList);

    // 使用 Lazy 作为 Query 的参数类型
    @Get("http://localhost:28080/basic01/info")
    String sendData(@Query("a") String a, @Query("b") String b, @Query("token") Lazy<String> token);

    @Request(
            url = "http://localhost:28080/basic01/info",
            headers = {
                    "Accept-Charset: utf-8",
                    "Content-Type: text/plain"
            }
    )
    String multipleHeaders();

    @Request(
            url = "http://localhost:28080/basic01/info",
            headers = {
                    "Accept-Charset: ${encoding}",
                    "Content-Type: text/plain"
            }
    )
    String bindingHeader(@Var("encoding") String encoding);

    @Request(
            url = "http://localhost:28080/basic01/info",
            headers = {
                    "User-Agent: ${userAgent}",
                    "Accept-Charset: utf-8",
                    "Content-Type: text/plain"
            }
    )
    String bindingHeader1(@Var("userAgent") String userAgent);


    /**
     * 使用 @Header 注解将参数绑定到请求头上
     *
     * @Header 注解的 value 指为请求头的名称，参数值为请求头的值
     * @Header("Accept") String accept将字符串类型参数绑定到请求头 Accept 上
     * @Header("accessToken") String accessToken将字符串类型参数绑定到请求头 accessToken 上
     */
    @Post("http://localhost:28080/basic01/info?username=foo")
    String postUser(@Header("Accept") String accept, @Header("accessToken") String accessToken);

    /**
     * 使用 @Header 注解可以修饰 Map 类型的参数
     * Map 的 Key 指为请求头的名称，Value 为请求头的值
     * 通过此方式，可以将 Map 中所有的键值对批量地绑定到请求头中
     */
    @Post("http://localhost:28080/basic01/info?username=foo")
    String headHelloUser(@Header Map<String, Object> headerMap);

    // 使用 Lazy 作为 Query 的参数类型
    @Post("http://localhost:28080/basic01/info?username=foo")
    String sendData1(@Query("a") String a, @Query("b") String b, @Header("token") Lazy<String> token);

    /**
     * 默认body格式为 application/x-www-form-urlencoded，即以表单形式序列化数据
     */
    @Post("http://localhost:28080/basic01/info")
    String sendPost(@Body("username") String username, @Body("password") String password);


    /**
     * 按键值对分别修饰不同的参数
     * 这时每个参数前的 @JSONBody 注解必须填上 value 属性或 name 属性的值，作为JSON的字段名称
     */
    @Post("http://localhost:28080/basic01/info")
    String helloUser(@JSONBody("username") String username, @JSONBody("password") String password);

    /**
     * 被@JSONBody注解修饰的List类型参数会被自定解析为JSON字符串
     */
    @Post("http://localhost:28080/basic01/info")
    String helloUserNames(@JSONBody List<String> usernames);

    /**
     * 被@JSONBody注解修饰的Map类型参数会被自定解析为JSON字符串
     */
    @Post("http://localhost:28080/basic01/info")
    String helloUser(@JSONBody Map<String, Object> user);

    /**
     * 被@JSONBody注解修饰的Map类型参数会被自定解析为JSON字符串
     */
    @HttpClient
//    @OkHttp3
    @Post(
            url = "http://localhost:28080/basic01/info",
            contentType = "application/json"
    )
    String helloUser1(@Body Map<String, Object> user);

    /**
     * 被@JSONBody注解修饰的Map类型参数会被自定解析为JSON字符串
     */
    @Backend("{0}")
//    @Backend("httpclient")
//    @Backend("okhttp3")
    // 关闭后端 Client 缓存
//    @BackendClient(cache = false)
    @Post(
            url = "http://localhost:28080/basic01/info",
            contentType = "application/json"
    )
    String helloUser2(String backend, @Body Map<String, Object> user);

    /**
     * 被@JSONBody注解修饰的Map类型参数会被自定解析为JSON字符串
     */
    @Post(
            url = "http://localhost:28080/basic01/info",
            contentType = "application/json",
            dataType = "json"
    )
    User helloUser3(@Body User user);

    /**
     * 被@JSONBody注解修饰的Map类型参数会被自定解析为JSON字符串
     */
    @Post(
            url = "http://localhost:28080/basic01/info",
            contentType = "application/json"
    )
    User helloUser4(@Body Map<String, Object> user);

    /**
     * Get类型请求，url路径为 /test
     * <p>ForestRequest是带泛型参数的类型
     * <p>泛型参数代表返回的响应数据所期望转化成的类型
     *
     * @return Forest请求对象
     */
    @Post(
            url = "http://localhost:28080/basic01/info",
            contentType = "application/json"
    )
    ForestRequest<?> getForestRequest(@Body Map<String, Object> user);

    /**
     * ForestResponse 可以作为请求方法的返回类型
     * ForestResponse 为带泛型的类，其泛型参数中填的类作为其响应反序列化的目标类型
     */
    @Post(
            url = "http://localhost:28080/basic01/info",
            contentType = "application/json"
    )
    ForestResponse<String> postUser(@Body Map<String, Object> user);

    /**
     * ForestResponse 可以作为请求方法的返回类型
     * ForestResponse 为带泛型的类，其泛型参数中填的类作为其响应反序列化的目标类型
     */
    @Post(
            url = "http://localhost:28080/basic01/info",
            contentType = "application/json"
    )
    ForestResponse<User> postUser1(@Body Map<String, Object> user);

    // maxRetryCount 为最大重试次数，默认为 0 次
    // maxRetryInterval 为最大重试时间间隔, 单位为毫秒，默认为 0 毫秒
    // @Retryer 注解绑定自定义的 MyRetryer 重试器类
    // 将按照自定义的重试策略处理重试间隔时间
    // 设置为自定义的 MySuccessCondition 条件类
    @Post(
//            url = "http://localhost:28080/basic01/info",
            url = "http://localhost:28080/basic01/info1",
            contentType = "application/json"
    )
    @Retry(maxRetryCount = "3", maxRetryInterval = "10000", condition = MyRetryCondition.class)
//    @Retry(maxRetryCount = "3", maxRetryInterval = "10000")
    @Retryer(MyRetryer.class)
    @Success(condition = MySuccessCondition.class)
    String postUser2(@Body Map<String, Object> user);

    // todo 自定义了重试条件，无论成功失败都会按照重试条件去做重试，但成功/失败用成功/失败条件实现类判断
    // maxRetryCount 为最大重试次数，默认为 0 次
    // maxRetryInterval 为最大重试时间间隔, 单位为毫秒，默认为 0 毫秒
    // @Retryer 注解绑定自定义的 MyRetryer 重试器类
    // 将按照自定义的重试策略处理重试间隔时间
    // 设置为自定义的 MySuccessCondition 条件类
    @Post(
            url = "http://localhost:28080/basic01/info",
//            url = "http://localhost:28080/basic01/info1",
            contentType = "application/json"
    )
    @Retry(maxRetryCount = "3", maxRetryInterval = "10000", condition = MyRetryCondition.class)
//    @Retry(maxRetryCount = "3", maxRetryInterval = "10000")
    @Retryer(MyRetryer.class)
    @Success(condition = MySuccessCondition.class)
    ForestResponse<String> postUser3(@Body Map<String, Object> user);

    @Post(
//            url = "http://localhost:28080/basic01/info",
            url = "http://localhost:28080/basic01/info1",
            contentType = "application/json"
    )
    String postUser4(@Body Map<String, Object> user, OnSuccess<String> onSuccess, OnError onError);

    /**
     * async 属性为 true 即为异步请求，为 false 则为同步请求
     * 不设置该属性时，默认为 false
     */
    @Post(
            url = "http://localhost:28080/basic01/info",
//            url = "http://localhost:28080/basic01/info1",
            contentType = "application/json",
            async = true
    )
    void asyncGet(@Body Map<String, Object> user, OnSuccess<String> onSuccess, OnError onError);


    /**
     * 登入接口(需要接受Cookie)
     */
    @Post(
            url = "http://localhost:28080/basic01/info",
            contentType = "application/json"
    )
    ForestResponse testLogin(@Body Map<String, Object> user, OnSaveCookie onSaveCookie);

    /**
     * 登入后测试接口(需要传入Cookie)
     */
    @Post(
            url = "http://localhost:28080/basic01/info",
            contentType = "application/json"
    )
    ForestResponse testAfterLogin(OnLoadCookie onLoadCookie);

    @Post(
//            url = "http://localhost:28080/basic01/info",
            url = "http://localhost:28080/basic01/info1",
            contentType = "application/json"
    )
    String postUser5(@Body Map<String, Object> user);

    @Post(
            url = "http://localhost:28080/basic01/info",
            contentType = "application/json",
            interceptor = {SimpleInterceptor.class}
    )
//    @Retry(maxRetryCount = "3", maxRetryInterval = "10000", condition = MyRetryCondition.class)
    String postUser6(@Body Map<String, Object> user);

    @Post(
            url = "http://localhost:28080/basic01/info",
            contentType = "application/json",
            interceptor = {SimpleInterceptor.class}
    )
    String postUser9(@Body User user);

    @Get(
            url = "http://localhost:28080/basic01/info",
            interceptor = {SimpleInterceptor.class}
    )
    String getUser1(@Query User user);

    @Post(
//            url = "http://localhost:28080/basic01/infoResult",
            url = "http://localhost:28080/basic01/infoResult1",
            contentType = "application/json"
    )
    ForestRequest<?> postUser7(@Body Map<String, Object> user);

    @Post(
//            url = "http://localhost:28080/basic01/infoResult",
            url = "http://localhost:28080/basic01/infoResult1",
            contentType = "application/json"
    )
    BaseResult<User> postUser8(@Body Map<String, Object> user);

    @Post(
            url = "http://localhost:28080/basic01/info",
            contentType = "application/json"
    )
    ForestRequest<?> postUser10(@Body Map<String, Object> user);

    @Post(
            url = "http://localhost:28080/basic01/info",
            contentType = "application/json"
    )
    ForestRequest<?> postUser11(@Body Dept dept);

}
